﻿using GameAnalyticsSDK;
using GoogleMobileAds.Api;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace HWGames.Bundles.Internal {
    public class AdsHandler : SingletonMonoBehavior<AdsHandler> {

        private const string SDK_NAME = "Admob";
        public const string AD_TOTAL_EVENT = "ad_aa_Total_Event";
#if UNITY_IOS
        private string rewardTestId = "ca-app-pub-3940256099942544/1712485313";
        private string interTestId = "ca-app-pub-3940256099942544/4411468910";
        private string bannerTestId = "ca-app-pub-3940256099942544/2934735716";
#else
        private string rewardTestId = "ca-app-pub-3940256099942544/5224354917";
        private string interTestId = "ca-app-pub-3940256099942544/1033173712";
        private string bannerTestId = "ca-app-pub-3940256099942544/6300978111";
#endif
        private string rewardId = "";
        private string interId = "";
        private string bannerId = "";

        #region 事件
        public event Action OnADInitEvent;

        public event Action OnInterLoadFailEvent;
        public event Action OnInterLoadSucceedEvent;
        public event Action OnInterShownEvent;
        public event Action OnInterSucceedShownEvent;
        public event Action OnInterFailedToShowEvent;
        public event Action OnInterCloseEvent;
        public event Action<AdValue, InterstitialAd> OnInterPaidEvent;

        public event Action OnRVLoadSucceedEvent;
        public event Action OnRVLoadFailedEvent;
        public event Action OnRVShownEvent;
        public event Action OnRVCloseEvent;
        public event Action OnRVRewardUserEvent;
        public event Action OnRVFailedToShowEvent;
        public event Action<AdValue, RewardedAd> OnRVPaidEvent;

        public event Action OnBannerLoadEvent;
        public event Action OnBannerLoadFailEvent;
        public event Action OnBannerClickEvent;
        public event Action OnBannerShownEvent;
        public event Action OnBannerCloseEvent;
        public event Action OnBannerAdHideEvent;
        public event Action<AdValue, BannerView> OnBannerPaidEvent;
        #endregion

        public bool IsBannerLoad { get; private set; } = false;
        public bool IsInitSuccess { get; private set; } = false;

        private bool isBannerShow = false;
        private List<WaitRequest> waitRequestList = new List<WaitRequest>();
        private Coroutine CoLoadReward;
        private Coroutine CoLoadInter;
        private Coroutine CoLoadBanner;
        private RewardedAd rewardedAd;
        private InterstitialAd interAd;
        private BannerView bannerAD;

        public void Init() {
            DontDestroyOnLoad(this);
            MobileAds.RaiseAdEventsOnUnityMainThread = true;
            MobileAds.Initialize(initStatus => {
                Debug.Log("【 MobileAds Initialize");
                LoadInterAD();
                LoadRewardAD();
            });
            rewardId = SDKBundle.GetRewardUnitID();
            interId = SDKBundle.GetInterUnitID();
            bannerId = SDKBundle.GetBannerUnitID();
            WaitRequest interRequest = new WaitRequest(RequestType.Inter);
            WaitRequest rewardRequest = new WaitRequest(RequestType.Reward);
            WaitRequest bannerRequest = new WaitRequest(RequestType.Banner);
            waitRequestList.Add(interRequest);
            waitRequestList.Add(rewardRequest);
            waitRequestList.Add(bannerRequest);
            OnADInitEvent?.Invoke();
            Debug.Log("【Ads Initialized】");
#if TEST_ADMOB
            //MediationTestSuite.Show();
#endif
            IsInitSuccess = true;
        }

        private void Update() {
            if (!IsInitSuccess) {
                return;
            }
            foreach (var item in waitRequestList) {
                item.Update();
            }
        }

        #region 插屏广告

        public void LoadInterAD() {
            if (CoLoadInter != null) {
                return;
            }
            CoLoadInter = HWCoDelegator.Coroutine(Co_Delay_Load_Inter());
        }

        IEnumerator Co_Delay_Load_Inter() {
            while (!IsReadyRequest(RequestType.Inter)) {
                yield return null;
            }
            if (interAd != null) {
                interAd.Destroy();
                interAd = null;
            }
            AdRequest request = new AdRequest();
            var adRequest = new AdRequest();
            InterstitialAd.Load(interId, adRequest,
            (InterstitialAd ad, LoadAdError error) => {
                if (error != null || ad == null) {
                    CoLoadInter = null;
                    HandleOnAdFailedToLoad(error);
                    return;
                }
                interAd = ad;
                if (HWGames.Bundles.Internal.HWSDKBundleBehaviour.IsCheckKey) {
#if !UNITY_EDITOR
                    GameAnalyticsILRD.SubscribeAdMobImpressions(interId, interAd);
#endif
                }
                CoLoadInter = null;
                HandleOnAdLoaded();
                RegisterEventHandlers(interAd);
            });
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Interstitial_Request");
            AddRequestDelayTime(RequestType.Inter);
            Debug.Log("【RequestInterAD");
        }

        private void RegisterEventHandlers(InterstitialAd ad) {
            ad.OnAdPaid += HandleInterAdPaid;
            ad.OnAdFullScreenContentOpened += HandleOnAdOpened;
            ad.OnAdFullScreenContentClosed += HandleOnAdClosed;
            ad.OnAdFullScreenContentFailed += HandleOnAdFailedToShow;
        }

        private void HandleInterAdPaid(AdValue obj) {
            OnInterPaidEvent?.Invoke(obj, interAd);
        }

        public bool IsInterstitialADReady() {
            if (interAd == null) {
                return false;
            }
            return interAd.CanShowAd();
        }

        public void ShowInterstitialAD() {
            if (IsInterstitialADReady()) {
                interAd.Show();
            }
            else
                LoadInterAD();
        }

        private void HandleOnAdLoaded() {
            Debug.Log("【HandleOnAdLoaded event received");
            OnInterLoadSucceedEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Interstitial_Loaded");
            RemoveRequestFailTime(RequestType.Inter);
        }

        private void HandleOnAdFailedToLoad(LoadAdError error) {
            Debug.Log("【HandleOnAdFailedToLoad event received with message: " + error.GetMessage());
            OnInterLoadFailEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Interstitial_LoadFailed", error.GetMessage());
            LoadInterAD();
            AddRequestFailTime(RequestType.Inter);
        }

        private void HandleOnAdOpened() {
            if (!HWGames.Bundles.Internal.HWSDKBundleBehaviour.IsCheckKey) {
                return;
            }
            Debug.Log("【HandleOnAdOpened event received");
            if (HWSDKTime.Instance.CountMinutes <= 50) {
                SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "minutes_Ad_Interstitial", HWSDKTime.Instance.CountMinutes);
            }
            string eventName = "ad_Total_Interstitial_Showed";
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, eventName);
            GameAnalytics.NewAdEvent(GAAdAction.Show, GAAdType.Interstitial, SDK_NAME, eventName);
            // SDKBundle.TrackFaceBookCustomEvent(eventName);
            SDKBundle.TrackFirebaseCustomEvent(eventName);
            SDKBundle.TrackSingularCustomEvent(eventName);
            OnInterShownEvent?.Invoke();
        }

        private void HandleOnAdFailedToShow(AdError adError) {
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Interstitial_FailedShow", adError.GetMessage());
        }

        private void HandleOnAdClosed() {
            Debug.Log("【HandleAdClosed event received");
            OnInterCloseEvent?.Invoke();
            LoadInterAD();
        }
        #endregion

        #region 激励广告

        public void LoadRewardAD() {
            if (CoLoadReward != null)
                return;
            CoLoadReward = HWCoDelegator.Coroutine(Co_Delay_Load_Reward());
        }

        IEnumerator Co_Delay_Load_Reward() {
            while (!IsReadyRequest(RequestType.Reward)) {
                yield return null;
            }
            if (rewardedAd != null) {
                rewardedAd.Destroy();
                rewardedAd = null;
            }
            AdRequest request = new AdRequest();
            RewardedAd.Load(rewardId, request,
           (RewardedAd ad, LoadAdError error) => {
               if (error != null || ad == null) {
                   CoLoadReward = null;
                   HandleRewardedAdFailedToLoad(error);
               }
               else {
                   rewardedAd = ad;
                   if (HWGames.Bundles.Internal.HWSDKBundleBehaviour.IsCheckKey) {
#if !UNITY_EDITOR
                       GameAnalyticsILRD.SubscribeAdMobImpressions(rewardId, rewardedAd);
#endif
                   }
                   CoLoadReward = null;
                   HandleRewardedAdLoaded();
                   RegisterEventHandlers(rewardedAd);
               }
           });
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Reward_Request");
            AddRequestDelayTime(RequestType.Reward);
            Debug.Log("【RequestRewardAD】");
        }

        private void RegisterEventHandlers(RewardedAd ad) {
            ad.OnAdPaid += HandleRewardedAdPaid;
            ad.OnAdFullScreenContentOpened += HandleRewardedAdOpening;
            ad.OnAdFullScreenContentClosed += HandleRewardedAdClosed;
            ad.OnAdFullScreenContentFailed += HandleRewardedAdFailedToShow;
        }

        private void HandleRewardedAdPaid(AdValue obj) {
            OnRVPaidEvent?.Invoke(obj, rewardedAd);
        }

        public bool IsRewardADReady() {
            if (rewardedAd == null) {
                return false;
            }
            return rewardedAd.CanShowAd();
        }

        public void ShowRewardAD() {
            if (IsRewardADReady())
                rewardedAd.Show((Reward reward) => {
                    HandleUserEarnedReward(reward);
                });
            else
                LoadRewardAD();
        }

        private void HandleRewardedAdLoaded() {
            Debug.Log("【HandleRewardedAdLoaded event received");
            OnRVLoadSucceedEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Reward_Loaded");
            RemoveRequestFailTime(RequestType.Reward);
        }

        public void HandleRewardedAdFailedToLoad(AdError error) {
            Debug.Log("【HandleRewardedAdFailedToLoad event received with message: " + error.GetMessage());
            OnRVLoadFailedEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Reward_LoadFailed", error.GetMessage());
            LoadRewardAD();
            AddRequestFailTime(RequestType.Reward);
        }

        private void HandleRewardedAdOpening() {
            if (!HWGames.Bundles.Internal.HWSDKBundleBehaviour.IsCheckKey) {
                return;
            }
            Debug.Log("【HandleRewardedAdOpening event received");
            if (HWSDKTime.Instance.CountMinutes <= 50) {
                SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "minutes_Ad_Reward", HWSDKTime.Instance.CountMinutes);
            }
            string eventName = "ad_Total_Reward_Showed";
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, eventName);
            GameAnalytics.NewAdEvent(GAAdAction.Show, GAAdType.RewardedVideo, SDK_NAME, eventName);
            // SDKBundle.TrackFaceBookCustomEvent(eventName);
            SDKBundle.TrackSingularCustomEvent(eventName);
            OnRVShownEvent?.Invoke();
        }

        public void HandleRewardedAdFailedToShow(AdError error) {
            Debug.Log("【HandleRewardedAdFailedToShow event received with message: " + error.GetMessage());
            OnRVFailedToShowEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Reward_FailedShow", error.GetMessage());
            LoadRewardAD();
        }

        public void HandleRewardedAdClosed() {
            Debug.Log("【HandleRewardedAdClosed event received");
            OnRVCloseEvent?.Invoke();
            LoadRewardAD();
        }

        private void HandleUserEarnedReward(Reward args) {
            Debug.Log("【HandleUserEarnedReward event received");
            OnRVRewardUserEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Reward_Done");
        }
        #endregion

        #region 横幅广告

        public void CreateBannerView() {
            if (bannerAD != null) {
                bannerAD.Destroy();
            }
            AdSize adaptiveSize = AdSize.GetCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(AdSize.FullWidth);
            bannerAD = new BannerView(bannerId, adaptiveSize, AdPosition.Bottom);
            if (HWGames.Bundles.Internal.HWSDKBundleBehaviour.IsCheckKey) {
#if !UNITY_EDITOR
                GameAnalyticsILRD.SubscribeAdMobImpressions(bannerId, bannerAD);
#endif
            }
            RegisterEventHandlers(bannerAD);
        }

        public void LoadBenner() {
            if (CoLoadBanner != null) {
                return;
            }
            CoLoadBanner = HWCoDelegator.Coroutine(Co_Delay_Load_Banner());
        }

        IEnumerator Co_Delay_Load_Banner() {
            while (!IsReadyRequest(RequestType.Banner)) {
                yield return null;
            }
            if (bannerAD == null) {
                CreateBannerView();
            }
            IsBannerLoad = false;
            AdRequest request = new AdRequest();
            bannerAD.LoadAd(request);
            AddRequestDelayTime(RequestType.Banner);
            Debug.Log("【RequestBannerAD");
            CoLoadBanner = null;
#if UNITY_EDITOR
            // ShowBanner();
#endif
        }

        public void HideBanner() {
            if (bannerAD == null)
                return;
            bannerAD.Hide();
            isBannerShow = false;
            OnBannerAdHideEvent?.Invoke();
        }

        public void ShowBanner() {
#if UNITY_ANDROID
            if (!SDKBundle.IsEnableAndroidBannerAd()) {
                return;
            }
#elif UNITY_IOS
            if(!SDKBundle.IsEnableIOSBannerAd()) {
                return;
            }
#endif
            if (isBannerShow) {
                return;
            }
            if (IsBannerLoad) {
                bannerAD.Show();
                isBannerShow = true;
                Debug.Log("【bannerAD show");
                OnBannerShownEvent?.Invoke();
            }
            else {
                isBannerShow = false;
                LoadBenner();
            }
        }

        public float GetBannerHeigh() {
            if (bannerAD == null) {
                return 50;
            }
            return bannerAD.GetHeightInPixels();
        }

        public void DestroyBanner() {
            bannerAD.Destroy();
            bannerAD = null;
        }

        private void RegisterEventHandlers(BannerView ad) {
            ad.OnAdPaid += HandleBannerPaid;
            ad.OnBannerAdLoaded += HandleBannerAdLoaded;
            ad.OnBannerAdLoadFailed += HandleBannerAdFailedToLoad;
            ad.OnAdFullScreenContentOpened += HandleBannerAdOpened;
            bannerAD.OnAdFullScreenContentClosed += HandleBannerAdClosed;
        }

        private void HandleBannerPaid(AdValue obj) {
            Debug.Log("【HandleBannerPaid event");
            OnBannerPaidEvent?.Invoke(obj, bannerAD);
            GameAnalytics.NewAdEvent(GAAdAction.Show, GAAdType.Banner, SDK_NAME, "ad_Total_Banner_Showed");
        }

        private void HandleBannerAdLoaded() {
            Debug.Log("【HandleBannerAdLoaded event received");
            IsBannerLoad = true;
            if (!isBannerShow) {
                isBannerShow = true;
                Debug.Log("【bannerAD show");
                OnBannerShownEvent?.Invoke();
            }
            OnBannerLoadEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Banner_Loaded");
        }

        private void HandleBannerAdFailedToLoad(AdError error) {
            Debug.Log("【HandleFailedToReceiveAd event received with message: " + error.GetMessage());
            OnBannerLoadFailEvent?.Invoke();
            SDKBundle.TrackCustomEvent(AD_TOTAL_EVENT, "ad_Total_Banner_LoadFailed", error.GetMessage());
            LoadBenner();
            AddRequestFailTime(RequestType.Banner);
        }

        private void HandleBannerAdOpened() {
            Debug.Log("【HandleBannerAdOpened event received");
            OnBannerClickEvent?.Invoke();
        }

        private void HandleBannerAdClosed() {
            Debug.Log("【HandleAdClosed event received");
            IsBannerLoad = false;
            OnBannerCloseEvent?.Invoke();
            LoadBenner();
        }
        #endregion

        #region Request
        private bool IsReadyRequest(RequestType requestType) {
            bool isReady = false;
            foreach (var item in waitRequestList.Where(x => x.CurType == requestType)) {
                isReady = item.IsReadyRequest();
            }
            return isReady;
        }

        private void AddRequestDelayTime(RequestType requestType) {
            foreach (var item in waitRequestList.Where(x => x.CurType != requestType)) {
                item.AddDelayTime();
            }
        }

        private void AddRequestFailTime(RequestType requestType) {
            foreach (var item in waitRequestList.Where(x => x.CurType == requestType)) {
                item.AddFailTime();
            }
        }

        private void RemoveRequestFailTime(RequestType requestType) {
            foreach (var item in waitRequestList.Where(x => x.CurType == requestType)) {
                item.RemoveFailTime();
            }
        }
        #endregion
    }

    public class WaitRequest {

        private float waitTime;

        public int RequestCount { get; set; }

        private readonly RequestType m_requestType;
        public RequestType CurType {
            get {
                return m_requestType;
            }
        }

        private const float DELAY_TIME = 1.0f;

        public WaitRequest(RequestType _requestType) {
            m_requestType = _requestType;
            RemoveFailTime();
        }

        public void Update() {
            if (waitTime > 0) {
                waitTime -= Time.deltaTime;
            }
        }

        public void AddDelayTime() {
            waitTime += DELAY_TIME;
        }

        public void AddFailTime() {
            waitTime += RequestCount == 0 ? 0 : Mathf.Pow(2, RequestCount - 1);
            RequestCount++;
        }

        public void RemoveFailTime() {
            RequestCount = 0;
            waitTime = 0;
        }

        public bool IsReadyRequest() {
            return waitTime <= 0;
        }
    }

    public enum RequestType {
        Inter,
        Reward,
        Banner,
    }
}